/*
* ============================================================================
*  Name     : CImageConverterEngine from ImageConverterEngine.cpp
*  Part of  : ImageConverter
*  Created  : May 14, 2006 by Forum Nokia
*  Implementation notes:
*     Handles all ICL interaction. Implemented as a separate class/engine
*     for the sake of clarity of the example.
*  Version  : 2.0
*  Copyright: Nokia Corporation
* ============================================================================
*/

#include "ImageConverterEngine.h"

#include <fbs.h>
#include <ImageConversion.h>
#include <BitmapTransforms.h>

const TInt KMaxInfoMsgLength = 50;

CImageConverterEngine* CImageConverterEngine::NewL(
    MConverterController* aController )
    {
    CImageConverterEngine* self =
        new(ELeave) CImageConverterEngine( aController );
    CleanupStack::PushL( self );

    self->ConstructL();

    CleanupStack::Pop(self);
    return self;
    }

CImageConverterEngine::CImageConverterEngine(
    MConverterController* aController ) :
    CActive( EPriorityStandard ), iController( aController )
    {
    iFrameCount = 0;
    iCurFrame = 0;
    iFrame= NULL;
    iFrameImg = NULL;
    iFrameMsk = NULL;
    iHeart = NULL;
    iBitmapDevice= NULL;
    iGraphicsContext= NULL;
    iImageDecoder = NULL;
    iContent = NULL;
    }

void CImageConverterEngine::ConstructL()
    {
    User::LeaveIfError( iFs.Connect() );
    iRotator = CBitmapRotator::NewL();
    iScaler = CBitmapScaler::NewL();
    iWait = new (ELeave) CActiveSchedulerWait();
    CActiveScheduler::Add( this );
    }

CImageConverterEngine::~CImageConverterEngine()
    {
    Cancel();
    delete iImageEncoder;
    delete iImageDecoder; // CImageDecoder must be deleted before the
    iFs.Close();          //   related RFs is closed,
    delete iFrameImg;		  //   otherwise a related thread might panic
    delete iFrameMsk;
    delete iRotator;
    delete iScaler;
    delete iWait;
    if (iHeart && iHeart->IsActive())
    	iHeart->Cancel();
    delete iHeart;
	delete iFrame;
	iFrame= NULL;
    delete iContent;
    iContent = NULL;
    delete iBitmapDevice;
	iBitmapDevice= NULL;
	delete iGraphicsContext;
	iGraphicsContext= NULL;
    }

void CImageConverterEngine::StartToDecodeL( TFileName& aFileName )
    {
    delete iImageDecoder;
    iImageDecoder = NULL;
    delete iFrame;
    iFrame= NULL;
    delete iFrameImg;
    iFrameImg = NULL;
    delete iFrameMsk;
    iFrameMsk = NULL;
    // create the decoder
    iImageDecoder = CImageDecoder::FileNewL( iFs, aFileName );
    iFrameCount = iImageDecoder->FrameCount();

    // create the destination bitmap
    iFrame = new (ELeave) CFbsBitmap();
    iFrame->Create( iImageDecoder->FrameInfo().iOverallSizeInPixels,
                     iImageDecoder->FrameInfo().iFrameDisplayMode );

	iFrameImg = new(ELeave)CFbsBitmap();
	iFrameImg->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels, iImageDecoder->FrameInfo(iCurFrame).iFrameDisplayMode);

	iFrameMsk = new (ELeave) CFbsBitmap;
    TBool transparent = ((iImageDecoder->FrameInfo().iFlags
            & TFrameInfo::ETransparencyPossible) != 0);
    if (transparent)
        iFrameMsk->Create(iImageDecoder->FrameInfo().iOverallSizeInPixels,EGray256);
    else
        iFrameMsk->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels,EGray2);
    // start conversion to bitmap
    iState = EDecoding;
	iImageDecoder->Convert(&iStatus, *iFrameImg, *iFrameMsk, iCurFrame);
    SetActive();
    }

void CImageConverterEngine::StartToDecodeL( HBufC8* aContent)
	{
    delete iImageDecoder;
    iImageDecoder = NULL;
    delete iFrame;
    iFrame= NULL;
    delete iFrameImg;
    iFrameImg = NULL;
    delete iFrameMsk;
    iFrameMsk = NULL;
    delete iContent;
    iContent = NULL;
    // create the decoder
    iContent = aContent;
    iImageDecoder = CImageDecoder::DataNewL(iFs, *iContent);
    iFrameCount = iImageDecoder->FrameCount();
    // create the destination bitmap
    iFrame = new (ELeave) CFbsBitmap();
    iFrame->Create( iImageDecoder->FrameInfo().iOverallSizeInPixels,
                     iImageDecoder->FrameInfo().iFrameDisplayMode );

	iFrameImg = new(ELeave)CFbsBitmap();
	iFrameImg->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels,
			iImageDecoder->FrameInfo(iCurFrame).iFrameDisplayMode);

    iFrameMsk = new (ELeave) CFbsBitmap;
    TBool transparent = ((iImageDecoder->FrameInfo().iFlags
            & TFrameInfo::ETransparencyPossible) != 0);
    if (transparent)
        iFrameMsk->Create(iImageDecoder->FrameInfo().iOverallSizeInPixels,EGray256);
    else
        iFrameMsk->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels,EGray2);
    // start conversion to bitmap
    iState = EDecoding;
    iImageDecoder->Convert(&iStatus, *iFrameImg, *iFrameMsk, iCurFrame);
    SetActive();
	}

void CImageConverterEngine::DecodeNextImageL()
	{
    if (iState != EIdle)
        {
        // Do nothing, if we are in the middle of processing a previous command
        return;
        }
    delete iFrameImg;
    iFrameImg = NULL;
    delete iFrameMsk;
    iFrameMsk = NULL;
    iCurFrame++;
    if (iCurFrame >= iFrameCount || iCurFrame < 0)
    	iCurFrame = 0;//������,�赱ǰ֡Ϊ0������һ��֡ Draw

    // create the destination bitmap
	iFrameImg = new(ELeave)CFbsBitmap();
	iFrameImg->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels,
			iImageDecoder->FrameInfo(iCurFrame).iFrameDisplayMode);

    iFrameMsk = new (ELeave) CFbsBitmap;
    TBool transparent = ((iImageDecoder->FrameInfo().iFlags
            & TFrameInfo::ETransparencyPossible) != 0);
    if (transparent)
        iFrameMsk->Create(iImageDecoder->FrameInfo().iOverallSizeInPixels,EGray256);
    else
        iFrameMsk->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels,EGray2);
    // start conversion to bitmap
    iState = EDecoding;
    iImageDecoder->Convert(&iStatus, *iFrameImg, *iFrameMsk, iCurFrame);
    SetActive();
	}

void CImageConverterEngine::StartToDecodeAsyL( TFileName& aFileName )
	{
    delete iImageDecoder;
    iImageDecoder = NULL;
    delete iFrame;
    iFrame= NULL;
    delete iFrameImg;
    iFrameImg = NULL;
    delete iFrameMsk;
    iFrameMsk = NULL;
    // create the decoder
    iImageDecoder = CImageDecoder::FileNewL( iFs, aFileName );
    iFrameCount = iImageDecoder->FrameCount();
    // create the destination bitmap
    iFrame = new (ELeave) CFbsBitmap();
    iFrame->Create( iImageDecoder->FrameInfo().iOverallSizeInPixels,
                     iImageDecoder->FrameInfo().iFrameDisplayMode );

	iFrameImg = new(ELeave)CFbsBitmap();
	iFrameImg->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels, iImageDecoder->FrameInfo(iCurFrame).iFrameDisplayMode);

    iFrameMsk = new (ELeave) CFbsBitmap;
    TBool transparent = ((iImageDecoder->FrameInfo().iFlags
            & TFrameInfo::ETransparencyPossible) != 0);
    if (transparent)
        iFrameMsk->Create(iImageDecoder->FrameInfo().iOverallSizeInPixels,EGray256);
    else
        iFrameMsk->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels,EGray2);
    // start conversion to bitmap
    iState = EDecoding;
    iImageDecoder->Convert(&iStatus, *iFrameImg, *iFrameMsk, iCurFrame);
    SetActive();
    iWait->Start();
    RunL();
	}

void CImageConverterEngine::StartToDecodeAsyL( const TDesC8& aContent )
	{
    delete iImageDecoder;
    iImageDecoder = NULL;
    delete iFrame;
    iFrame= NULL;
    delete iFrameImg;
    iFrameImg = NULL;
    delete iFrameMsk;
    iFrameMsk = NULL;
    delete iContent;
    iContent = NULL;
    // create the decoder
    iContent = aContent.AllocL();
    iImageDecoder = CImageDecoder::DataNewL(iFs, *iContent);
    iFrameCount = iImageDecoder->FrameCount();
    // create the destination bitmap
    iFrame = new (ELeave) CFbsBitmap();
    iFrame->Create( iImageDecoder->FrameInfo().iOverallSizeInPixels,
                     iImageDecoder->FrameInfo().iFrameDisplayMode );

	iFrameImg = new(ELeave)CFbsBitmap();
	iFrameImg->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels, iImageDecoder->FrameInfo(iCurFrame).iFrameDisplayMode);

    iFrameMsk = new (ELeave) CFbsBitmap;
    TBool transparent = ((iImageDecoder->FrameInfo().iFlags
            & TFrameInfo::ETransparencyPossible) != 0);
    if (transparent)
        iFrameMsk->Create(iImageDecoder->FrameInfo().iOverallSizeInPixels,EGray256);
    else
        iFrameMsk->Create(iImageDecoder->FrameInfo(iCurFrame).iOverallSizeInPixels,EGray2);
    // start conversion to bitmap
    iState = EDecoding;
    iImageDecoder->Convert(&iStatus, *iFrameImg, *iFrameMsk, iCurFrame);
    SetActive();
    iWait->Start();
    RunL();
	}

void CImageConverterEngine::StartToEncodeL(
    TFileName& aFileName, TUid aImageType, TUid aImageSubType )
    {
    if (iImageEncoder)
        {
        delete iImageEncoder;
        iImageEncoder = NULL;
        }

    // create the encoder
    iImageEncoder = CImageEncoder::FileNewL( iFs, aFileName,
        CImageEncoder::EOptionNone, aImageType, aImageSubType );

    iState = EEncoding;
    iImageEncoder->Convert( &iStatus, *iFrame );
    SetActive();
    }

void CImageConverterEngine::DoCancel()
    {
    if( iState == EDecoding )
        {
        iImageDecoder->Cancel();
        }
    else if( iState == EEncoding )
        {
        iImageEncoder->Cancel();
        }
    else if( iState == ERotating )
        {
        iRotator->Cancel();
        }
    else if( iState == EScaling )
        {
        iScaler->Cancel();
        }
    else
        {
        // State is EIdle, do nothing
        }
    if (iWait->IsStarted())
    	iWait->AsyncStop();
    if (iHeart && iHeart->IsActive())
    	iHeart->Cancel();
    }


void CImageConverterEngine::RunL()
    {
    if (iWait->IsStarted())
    	iWait->AsyncStop();
    switch( iState )
        {
        case EDecoding:
            {
            if( iStatus == KErrNone )
                {
                iState = EIdle;
                _LIT(KImageReady, "Image ready");
                DrawFrameOffScreenL();
                if (iController)
                    iController->NotifyCompletion( KErrNone, KImageReady );
                if (iFrameCount > 1)
                	StartTimer(800000);
                break;
                }
            else if( iStatus == KErrUnderflow )
                {
                iImageDecoder->ContinueConvert( &iStatus );
                SetActive();
                break;
                }
            else if ( iStatus == KErrCorrupt )
                {
                iState = EIdle;
                _LIT(KImageCorrupt, "Image file corrupted");
                if (iController)
                    iController->NotifyCompletion( KErrCorrupt, KImageCorrupt );
                break;
                }
            else
                {
                // Unknown error
                _LIT( KErrorMsg, "Error while opening image:%d" );
                TBuf<KMaxInfoMsgLength> buf;
                buf.Format( KErrorMsg, iStatus.Int() );
                if (iController)
                    iController->NotifyCompletion( iStatus.Int(), KErrorMsg );
                break;
                }
            }
        case EEncoding:
            {
            _LIT(KImageSaved, "Image saved");
            iState = EIdle;
            if (iController)
                iController->NotifyCompletion( iStatus.Int(), KImageSaved);
            break;
            }
        case ERotating:
        case EScaling:
        	if( iStatus == KErrNone )
	        	{
	            iState = EIdle;
	            _LIT(KImageReady, "Image ready");
	            if (iController)
	                iController->NotifyCompletion( KErrNone, KImageReady );
	            break;
	            }
//        default:
//            {
//            iState = EIdle;
//            iController->NotifyCompletion( iStatus.Int(), KNullDesC );
//            }
        }
    }

TInt CImageConverterEngine::RunError(TInt /*aError*/)
    {
    // Our RunL does not contain any method calls that would leave, so this method
    // should never be called.
    return KErrNone;
    }

void CImageConverterEngine::GetEncoderImageTypesL(
    RImageTypeDescriptionArray& aImageTypeArray )
    {
    CImageEncoder::GetImageTypesL( aImageTypeArray );
    }

CFrameInfoStrings* CImageConverterEngine::GetImageInfoL()
    {
    // notice that the information is returned from the original image
    //  hence the scaling of the bitmap does not affect the info until
    //  the bitmap is encoded back to image file and opened again
    if( iImageDecoder )
        {
        return( iImageDecoder->FrameInfoStringsL() );
        }
    else
        {
        return NULL ;
        }
    }

void CImageConverterEngine::Rotate( TDirection aDirection )
    {
    if (iState != EIdle)
        {
        // Do nothing, if we are in the middle of processing a previous command
        return;
        }

    if( aDirection == EClockwise90 )
        {
        iRotator->Rotate( &iStatus, *iFrame,
            CBitmapRotator::ERotation90DegreesClockwise );
        }
    else
        {
        // 270 clockwise == 90 anticlockwise
        iRotator->Rotate( &iStatus, *iFrame,
            CBitmapRotator::ERotation270DegreesClockwise );
        }

    iState = ERotating;
    SetActive();
    }

void CImageConverterEngine::Scale( TInt aPercent )
    {
    if (iState != EIdle)
        {
        // Do nothing, if we are in the middle of processing a previous command
        return;
        }

    TReal32 multiplier;

    multiplier = aPercent/(TReal32)100.0;

    TInt newWidth = (TInt)(iFrame->SizeInPixels().iWidth * multiplier);
    TInt newHeight = (TInt)(iFrame->SizeInPixels().iHeight * multiplier);

    // the maintain aspect ratio is by default set to true
    iScaler->Scale( &iStatus, *iFrame, TSize(newWidth,newHeight) );

    iState = EScaling;
    SetActive();
    }

void CImageConverterEngine::ScaleAsy( TInt aPercent )
    {
    if (iState != EIdle)
        {
        // Do nothing, if we are in the middle of processing a previous command
        return;
        }

    TReal32 multiplier;

    multiplier = aPercent/(TReal32)100.0;

    TInt newWidth = (TInt)(iFrame->SizeInPixels().iWidth * multiplier);
    TInt newHeight = (TInt)(iFrame->SizeInPixels().iHeight * multiplier);

    // the maintain aspect ratio is by default set to true
    iScaler->Scale( &iStatus, *iFrame, TSize(newWidth,newHeight) );

    iState = EScaling;
    SetActive();
    
    iWait->Start();
    RunL();
    }

void CImageConverterEngine::Scale( TSize aNewSize )
	{
    if (iState != EIdle)
        {
        // Do nothing, if we are in the middle of processing a previous command
        return;
        }
    // the maintain aspect ratio is by default set to true
    iScaler->Scale( &iStatus, *iFrame, aNewSize, EFalse);

    iState = EScaling;
    SetActive();
	}

CFbsBitmap* CImageConverterEngine::GetBitmap()
    {
    return iFrame;
    }

CFbsBitmap* CImageConverterEngine::GetMaskBitmap()
    {
    return iFrameMsk;
    }

void CImageConverterEngine::StartTimer(TInt aTickInterval)
	{
	if (!iHeart)
		iHeart=CPeriodic::NewL(0); // neutral priority
	if (iHeart->IsActive())
		iHeart->Cancel();
	//TInt TickInterval = 150000;
	iHeart->Start(aTickInterval, aTickInterval, TCallBack(Tick, this));
	}

TInt CImageConverterEngine::Tick(TAny* aObject)
	{
	static_cast<CImageConverterEngine*>(aObject)->DecodeNextImageL();
	return 0;
	}

void CImageConverterEngine::DrawFrameOffScreenL()
	{
   //����λͼ�豸
	iBitmapDevice = CFbsBitmapDevice::NewL(iFrame);
	//Ϊ����λͼ����ͼ��������
	User::LeaveIfError(iBitmapDevice->CreateContext(iGraphicsContext));
	if (iFrameMsk && iFrameImg && iFrame && iGraphicsContext)
		{
        //�þ�����õ�DELAYTIMEʱ��
		if (iFrameMsk->Handle() && iFrameImg->Handle() && iFrame->Handle())
			{
			if (TFrameInfo::ERestoreToBackground & iImageDecoder->FrameInfo(iCurFrame).iFlags)
				{
				iGraphicsContext->DrawBitmap(
						TRect(0, 0,iFrame->SizeInPixels().iWidth,iFrame->SizeInPixels().iHeight),
						iFrameImg);
				}
			else
				{
				iGraphicsContext->DrawBitmapMasked(iImageDecoder->FrameInfo(iCurFrame).iFrameCoordsInPixels,
						iFrameImg, TRect(0, 0, iFrameImg->SizeInPixels().iWidth,iFrameImg->SizeInPixels().iHeight),
						iFrameMsk, EFalse);
				}
			}
		}
    delete iBitmapDevice;
	iBitmapDevice= NULL;
	delete iGraphicsContext;
	iGraphicsContext= NULL;
	}
